T がneverの時の、T extends .. は、問答無用でneverになる
何を言っているか?
code:ts
type A<T> = T extends never ? true : false;
こういう結果になる
code:ts
type B = A<never>; // never
? true : falseだけ見ると、trueかfalseが返ってきそうだが、
何故かneverが返る
ポイント
だとすると、任意のunion型は無限にneverが続き、無限に分配されることになる
従って、分配過程で登場するnever extends ..は問答無用でneverを返す
なぜか?
union型内のneverは「空のunion型」として扱われる
例えば、
'a' | neverは、 'a'になる
'a' | (never | 'b') | (never | never)は、'a' | 'b' になる
ということは逆に、'a'|'b'は以下全てと同値
'a'|'b'|never
'a'|'b'|never|never
never|'a'|never|'b'|never|never
...
code:ts
type D<T> = T extends 'a' | 'b' | 'c' ? true : false;
type A = D<'d'>; // false
簡約過程は以下のようになる
code:ts
type A = D<'d'>;
= 'd' extends 'a' | 'b' | 'c' ? true : false;
= false;
'd'は、'd'|neverと同じなので、これをDに適用した結果も同じになるはず
同様に簡約過程を見てみる
code:ts
type A = D<'d' | never>;
= 'd' extends 'a' | 'b' | 'c' ? true : false
| never extends 'a' | 'b' | 'c' ? true : false; // ①
= false | never; // ②
= false;
D<'d'>と、D<'d' | never>が同じ結果になるためには、
①の行が、neverに簡約されないと困る
この仕様を知らなければ、①は直感的にはtrueと簡約されそうmrsekut.icon
なぜならnerver型はbottom型なので、型引数としなければ、↓のようになる code:ts
type _ = never extends 'a' | 'b' | 'c' ? true : false; // true
そうなると、②がfalse|trueとなり、
type A = booleanになってD<'d'>のときと結果が異なってしまう
例が微妙だったので変えたmrsekut.icon↑ ref ちなみに、型引数にするかどうかで挙動が変わる
直接的にnever extends neverと書いた場合は真
code:ts
type A = never extends never ? true : false; // true
型引数にした場合はnever
code:ts
type A<T> = T extends never ? true : false;
type B = A<never>; // never
だからこのノートのタイトルには「T がneverの時の、」という条件を入れている
ちなみに、extends ココは何でも良い
どうせ解釈されないので。
だから最初のコード例はこう書いても同じ
code:ts
type A<T> = T extends any ? true : false;
code:ts
type A<T> = T extends string ? true : false;
code:ts
type A<T> = T extends T ? true : false;
...
いずれもこうなる
code:ts
type B = A<never>; // never
当たり前だが、Tにnever以外が代入された時の挙動は異なるmrsekut.icon
意図を明示するためにneverと書くのが良いかも
良くないけど。最善策という感じがするmrsekut.icon
stringとか書くと意図が読み取れないmrsekut.icon
では、「型引数Tがneverである」のような条件分岐を書きたい場合はどうするか?
上の話だと、単純にT extends never ? .. : ..と書くと、?節や:節に入ってくれない
T[] extends never[]のように書けばいい